home *** CD-ROM | disk | FTP | other *** search
/ 2,000 Greater & Lesser Mysteries / 2,000 Greater and Lesser Mysteries.iso / psychol / marble / mmpi.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-10  |  13.3 KB  |  686 lines

  1. #include "MMPI.HPP"
  2. #include <string.h>
  3. #include <ctype.h>
  4. #include <alloc.h>
  5. #include <stdio.h>
  6.  
  7. // Labels for ASCII test file
  8. char *aScale[NUM_SCALES] = {
  9.     "Q",        // ?    Unanswered question
  10.     "F",        // F     Validity
  11.     "K",        // K     Correction
  12.     "Hs",        // Hs    Hypochondriasis
  13.     "D",        // D     Depression
  14.     "Hy",        // Hy    Hysteria
  15.     "Pd",        // Pd    Psychopathic deviant
  16.     "MfM",    // Mf    Masculinity-femininity    Male
  17.     "MfF",    // Mf    Masculinity-femininity    Female
  18.     "Pa",        // Pa    Paranoia
  19.     "Pt",        // Pt    Psychasthenia
  20.     "Sc",        // Sc    Schizophrenia
  21.     "Ma",        // Ma    Hypomania
  22.     "Si",        // Si    Social introversion
  23.     "L"        // L     Lie
  24. };
  25.  
  26. //
  27. //    MMPIQuestion definitions
  28. //
  29.  
  30. MMPIQuestion::MMPIQuestion(char *str)
  31. {
  32.     text = strdup(str);
  33.     clearScales();
  34.     response = 'Q';
  35. }
  36.  
  37. MMPIQuestion::~MMPIQuestion()
  38. {
  39.     if(text)
  40.         free(text);
  41.     text = NULL;
  42. }
  43.  
  44. void MMPIQuestion::clearScales()
  45. {
  46.     memset(scale, ' ', NUM_SCALES);
  47. }
  48.  
  49. int MMPIQuestion::setScale(char *key)
  50. {
  51.     int c;
  52.     char *keyword;
  53.     int    keywordLen;
  54.     char *scaleResponse;
  55.  
  56.     keyword=key;
  57.  
  58.     // Parse out keyword and setting
  59.     while(*keyword && isspace(*keyword))
  60.         ++keyword;
  61.     scaleResponse = keyword;
  62.     while(*scaleResponse && isalnum(*scaleResponse))
  63.         ++scaleResponse;
  64.     keywordLen = (int)(scaleResponse - keyword);
  65.     while(*scaleResponse && isspace(*scaleResponse))
  66.         ++scaleResponse;
  67.     *scaleResponse = toupper(*scaleResponse);
  68.  
  69.     if((*scaleResponse != 'T') && (*scaleResponse != 'F'))
  70.         return 0;    // Not a valid scale response
  71.  
  72.     for(c=0;c<NUM_SCALES;++c) {
  73.         if(!strnicmp(keyword,aScale[c],keywordLen)) {
  74.             scale[c]=*scaleResponse;
  75.             return 1;
  76.             }
  77.     }
  78.     return 0; // Not a valid keyword definition
  79. }
  80.  
  81. // Ask and AskCheat are defined in USER.CPP
  82. char MMPIQuestion::getScale(int which)
  83. {
  84.     if( which >= 0 && which < NUM_SCALES )
  85.         return scale[which];
  86.     else
  87.         return ' ';
  88. }
  89.  
  90. MMPITotal MMPIQuestion::eval()
  91. {
  92.     MMPITotal tot;
  93.     int c;
  94.  
  95.     if(response == 'Q')
  96.         tot.total[Q] = 1;
  97.     else
  98.         for(c=F;c<NUM_SCALES;++c)
  99.             tot.total[c] = (scale[c]==response);
  100.     return tot;
  101. }
  102.  
  103. //
  104. //    MMPITotal definitions
  105. //
  106.  
  107. MMPITotal::MMPITotal()
  108. {
  109.     clear();
  110. }
  111.  
  112. void MMPITotal::clear()
  113. {
  114.     memset(total, 0, NUM_SCALES * sizeof(short));
  115. }
  116.  
  117. // Add results calculated by a question eval
  118. MMPITotal& operator+= (MMPITotal& tot, MMPITotal& amount)
  119. {
  120.     int c;
  121.  
  122.     for(c=0;c<NUM_SCALES;++c)
  123.         tot.total[c]+=amount.total[c];
  124.  
  125.     return tot;
  126. }
  127.  
  128. // Returns values adjusted by K scale
  129. MMPITotal MMPITotal::getCorrection()
  130. {
  131.     MMPITotal ret;
  132.     int k = total[K];
  133.  
  134.     ret.total[Hs] = (k/2);
  135.     ret.total[Pd] = ((k*4)/10);
  136.     ret.total[Pt] = k;
  137.     ret.total[Sc] = k;
  138.     ret.total[Ma] = ((k*2)/10);
  139.  
  140.     return ret;
  141. }
  142.  
  143. // Create a result scale for display
  144. MMPITotal MMPITotal::scale(int gender)
  145. {
  146.     MMPITotal rslt;
  147.  
  148.     // Return values start at 0 (rock bottom) and proceed upward to 127
  149.  
  150.     rslt.total[L]        =    36    + (total[L]     *3);
  151.     rslt.total[F]        =    44    + (total[F]     *2);
  152.     rslt.total[K]        =    26    + (total[K]     *19 / 10);
  153.     rslt.total[Ma]        =    11    + (total[Ma]    *25 / 10);
  154.     rslt.total[Si]        =    20    + (total[Si]    *21 / 20);
  155.     rslt.total[Pa]        =    27    + (total[Pa]    *29 / 10);
  156.  
  157.     rslt.total[Mf_M]    =    12+ (total[Mf_M] * 19 / 10);
  158.     rslt.total[Mf_F]    = 126- (total[Mf_F] * 21 / 10); // Essentially inverted
  159.  
  160.     if (gender == MALE) {
  161.         rslt.total[Hs]    =    21    + (total[Hs] * 26 / 10);
  162.         rslt.total[D]    =    10    + (total[D]  * 24 / 10);
  163.         rslt.total[Hy]    =    26    + (total[Hy] * 18 / 10);
  164.         rslt.total[Pd]    =     7    + (total[Pd] * 23 / 10);
  165.         rslt.total[Pt]    =     2    + (total[Pt] * 21 / 10);
  166.         rslt.total[Sc]    =     6    + (total[Sc] * 19 / 10);
  167.     }
  168.     else {
  169.         rslt.total[Hs]    =    23    + (total[Hs] * 21 / 10);
  170.         rslt.total[D]    =    13    + (total[D]  * 19 / 10);
  171.         rslt.total[Hy]    =    16    + (total[Hy] * 18 / 10);
  172.         rslt.total[Pd]    =     5    + (total[Pd] * 24 / 10);
  173.         rslt.total[Pt]    =     9    + (total[Pt] * 16 / 10);
  174.         rslt.total[Sc]    =     8    + (total[Sc] * 15 / 10);
  175.     }
  176.  
  177.     return rslt;
  178. }
  179.  
  180.  
  181. //
  182. //    MMPITest definitions
  183. //
  184.  
  185. MMPINode::MMPINode(MMPIQuestion *obj)
  186. {
  187.     dat = obj;
  188.     next = NULL;
  189.     prev = NULL;
  190. }
  191.  
  192. MMPIQuestion *MMPINode::unlink()
  193. {
  194.     if(prev)
  195.         prev->next = next;
  196.     if(next)
  197.         next->prev = prev;
  198.     return dat;
  199. }
  200.  
  201.  
  202. MMPITest::MMPITest(char *file)
  203. {
  204.     curr = head = tail = NULL;
  205.     size = 0;    
  206.     results.clear();
  207.     Scale.clear();
  208.     Adjusted.clear();
  209.     Correction.clear();
  210.     gender = 0;
  211.     total = 0;
  212.     load(file);
  213. }
  214.  
  215. MMPITest::~MMPITest()
  216. {
  217.     flush();
  218. }
  219.  
  220. MMPINode *MMPITest::find(MMPIQuestion *srch)
  221. {
  222.     MMPINode *n;
  223.  
  224.     n=head;
  225.     while(n) {
  226.         if(n->dat == srch) {
  227.             curr = n;
  228.             return n;
  229.             }
  230.         n = n->next;
  231.         }
  232.     
  233.     return NULL;
  234. }
  235.  
  236. MMPINode *MMPITest::goPosn(int p)
  237. {
  238.     MMPINode *n;
  239.  
  240.     n=head;
  241.  
  242.     while(n && p) {
  243.         n=n->next;
  244.         p--;
  245.         }
  246.     curr = n;
  247.     return n;
  248. }
  249.  
  250. MMPINode* MMPITest::add(MMPIQuestion *obj)
  251. {
  252.     MMPINode* newNode;
  253.  
  254.     if(find(obj))
  255.         return NULL;
  256.  
  257.     newNode = new MMPINode(obj);
  258.     ++ size;
  259.  
  260.     if( !tail ) {
  261.         head = tail = curr = newNode;
  262.     }
  263.     else {
  264.         newNode->prev = tail;
  265.         tail->next = newNode;
  266.         tail = newNode;
  267.         curr = newNode;
  268.     }
  269.     return newNode;
  270. }
  271.  
  272. void MMPITest::del(MMPIQuestion *obj)
  273. {
  274.     MMPINode* foundNode;
  275.  
  276.     if( foundNode == find(obj) ) {
  277.         foundNode->unlink();
  278.         
  279.         if(foundNode == head)
  280.             head = foundNode->next;
  281.         if(foundNode == tail)
  282.             tail = foundNode->prev;
  283.         if(foundNode == curr)
  284.             curr = (foundNode->next)? foundNode->next : tail;
  285.         delete foundNode->dat;
  286.         delete foundNode;
  287.         -- size;
  288.     }
  289. }
  290.  
  291. MMPIQuestion*    MMPITest::first()
  292. {
  293.     curr = head;
  294.     if(head)
  295.         return head->dat;
  296.     return NULL;
  297. }
  298.  
  299. MMPIQuestion*    MMPITest::last()
  300. {
  301.     curr = tail;
  302.     if(tail)
  303.         return tail->dat;
  304.     return NULL;
  305. }
  306.  
  307. MMPIQuestion*    MMPITest::at(int pos)
  308. {
  309.     MMPINode *n;
  310.  
  311.     n = goPosn(pos);
  312.     if(n) {
  313.         curr = n;
  314.         return n->dat;
  315.         }
  316.     return NULL;
  317. }
  318.          
  319. MMPIQuestion*    MMPITest::next()
  320. {
  321.     if(curr->next) {
  322.         curr = curr->next;
  323.         return (curr->dat);
  324.         }
  325.     return NULL;
  326. }
  327.  
  328. MMPIQuestion*    MMPITest::prev()
  329. {
  330.     if(curr->prev) {
  331.         curr = curr->prev;
  332.         return (curr->dat);
  333.         }
  334.     return NULL;
  335. }
  336.  
  337. MMPIQuestion*    MMPITest::current()
  338. {
  339.     if(curr)
  340.         return curr->dat;
  341.    else
  342.        return NULL;
  343. }
  344.  
  345. void MMPITest::flush()
  346. {
  347.     MMPINode *cObj = head;
  348.     MMPINode *nObj;
  349.  
  350.     while(cObj) {
  351.         nObj = cObj->next;
  352.         delete cObj->dat;
  353.         delete cObj;
  354.         cObj = nObj;
  355.         }
  356.     curr = head = tail = NULL;
  357.     size = 0;
  358. }
  359.  
  360.  
  361. // Evaluate test responses
  362. int MMPITest::eval()
  363. {
  364.     MMPIQuestion *current;
  365.     
  366.     if(isNotComplete())
  367.       return 0;
  368.    
  369.    results.clear();
  370.     Correction.clear();
  371.     Adjusted.clear();
  372.     Scale.clear();
  373.     
  374.     current = first();
  375.     while(current) {
  376.         results += current->eval();
  377.         current = next();
  378.         }
  379.     
  380.    Correction = results.getCorrection();
  381.     Adjusted = results;
  382.     Adjusted += Correction;
  383.     Scale = Adjusted.scale(gender);
  384.     return 1;
  385. }
  386.  
  387. int MMPITest::isEmpty()
  388. {
  389.    return( head->dat->response == 'Q' );
  390. }
  391.  
  392. MMPIQuestion* MMPITest::isNotComplete()
  393. {
  394.     MMPIQuestion *current;
  395.  
  396.    if(isEmpty())
  397.         return NULL;
  398.  
  399.    // Find the first unanswered question
  400.     current = first();
  401.     while( current ) {
  402.         if( current->response == 'Q' )
  403.             break;
  404.         current = next();
  405.         }
  406.    
  407.     return current;
  408. }
  409.  
  410.  
  411.  
  412.  
  413. //
  414. //    Load file rules:
  415. //
  416. //        Skip all lines beginning with a '#'
  417. //        Question definition begins with an empty line.
  418. //            Get first line of text
  419. //                Concatenate next line if the current line ends with a space
  420. //                 ...
  421. //            Scale definitions immediately follow text a definition
  422. //                Format is: ScaleLabel(white space)T or F
  423. //        Question defs continue to EOF
  424. //
  425. //        (Examine the test file for examples.)
  426. //
  427. int MMPITest::load(char *file)
  428. {
  429. #define BufSize 256
  430. #define TextMax 1024
  431. enum LoadStates {
  432.     SCANNING,
  433.     GET_TEXT,
  434.     APP_TEXT,
  435.     GET_SCALE,
  436. };
  437.  
  438.     ifstream inf;
  439.     
  440.     inf.open(file, ios::in);
  441.  
  442.     if( inf ) {
  443.         char buf[BufSize];
  444.         char textBuf[TextMax];
  445.         int state = SCANNING;
  446.         int bufSize;
  447.         MMPIQuestion *newQ;
  448.  
  449.         while(inf.getline(buf, BufSize)) {
  450.             if (*buf == '#')
  451.                 continue;
  452.             bufSize = strlen(buf);
  453.             buf[BufSize - 1]='\0';
  454.             switch(state) {
  455.  
  456.                 case SCANNING:
  457.                     if(!bufSize)
  458.                         state = GET_TEXT;
  459.                     break;
  460.  
  461.                 case GET_TEXT:
  462.                     strcpy(textBuf, buf);
  463.                     if(buf[bufSize - 1] == ' ')
  464.                         state = APP_TEXT;
  465.                     else {
  466.                         newQ = new MMPIQuestion( textBuf );
  467.                         add(newQ);
  468.                         state = GET_SCALE;
  469.                         }
  470.                     break;
  471.  
  472.                 case APP_TEXT:
  473.                     strcat(textBuf, buf);
  474.                     if(buf[bufSize - 1] != ' ') {
  475.                         newQ = new MMPIQuestion( textBuf );
  476.                         add(newQ);
  477.                         state = GET_SCALE;
  478.                         }
  479.                     break;
  480.  
  481.  
  482.                 case GET_SCALE:
  483.                     if(! newQ->setScale(buf)) {
  484.                         if(bufSize)
  485.                             state = SCANNING;
  486.                         else
  487.                             state = GET_TEXT;
  488.                     }
  489.                     break;
  490.                 };
  491.  
  492.             }
  493.         }
  494.     else
  495.         return 0;
  496.     inf.close();
  497.     clearAnswers();
  498.     return 1;
  499. }
  500.  
  501.  
  502. void MMPITest::clearAnswers()
  503. {
  504.     MMPIQuestion *current;
  505.  
  506.     current = first();
  507.     while(current)    {
  508.         current->response = 'Q';
  509.         current = next();
  510.         }
  511.     results.clear();
  512.     Correction.clear();
  513.     Scale.clear();
  514.     Adjusted.clear();
  515.     gender = 0;
  516.    first();
  517. }
  518.  
  519. //
  520. // Answer file rules:
  521. //
  522. //        Skip all lines beginning with a '#'
  523. //        First line:        Test file name
  524. //        Second line:    Name
  525. //        Third line:        Sex Age
  526. //        T/F answers to EOF
  527. //
  528.  
  529.  
  530. int MMPITest::saveAnswers(char *path)
  531. {
  532.     MMPIQuestion *current;
  533.     int answer;
  534.     ofstream onf;
  535.     
  536.     onf.open(path, ios::out);
  537.     
  538.     if(onf) {
  539.         onf << gender;
  540.       current = first();
  541.         while(onf && current) {
  542.             switch(current->response) {
  543.                 case 'T':
  544.                     onf << 'T';
  545.                     break;
  546.                 case 'F':
  547.                     onf << 'F';
  548.                     break;
  549.                 default:
  550.                     onf << 'Q';
  551.                     break;
  552.                 }
  553.             current = next();
  554.             }
  555.         }
  556.     else
  557.         // Unable to save file
  558.         return 0;
  559.     onf.close();
  560.     return 1;
  561. }
  562.  
  563. int MMPITest::loadAnswers(char *path)
  564. {
  565.     MMPIQuestion *current;
  566.     int answer;
  567.     char resp;
  568.    ifstream inf;
  569.  
  570.     inf.open(path, ios::in);
  571.     if(inf) {
  572.         clearAnswers();
  573.         gender = inf.get();
  574.         if(gender != 'M' && gender != 'F') {
  575.           inf.close();
  576.            return 0;
  577.       }
  578.  
  579.       current = first();
  580.         while(inf && current) {
  581.             resp = inf.get();
  582.          current->response = (resp != 'T' && resp != 'F') ? 'Q' : resp;
  583.             current = next();
  584.             }
  585.         }
  586.     else
  587.         return 0;
  588.  
  589.     inf.close();
  590.     return 1;
  591. }
  592.  
  593. int MMPITest::ResultForm(char *fName)
  594. {
  595.     if(!eval())
  596.       return 0;
  597.  
  598.     FILE *fp;
  599.    if( (fp = fopen(fName, "w")) == NULL )
  600.       return 0;
  601.  
  602.    fprintf(fp,"                  Test Summary\n");
  603.    fprintf(fp,"------------------------------------------------\n");
  604.     fprintf(fp,"   Q      Question:                        %d\n", Adjusted.total[Q]);
  605.     fprintf(fp,"   F      Validity:                        %d\n", Adjusted.total[F]);
  606.     fprintf(fp,"   K      Correction:                      %d\n", Adjusted.total[K]);
  607.    fprintf(fp,"   Hs+.5K Hypochondriasis, adjusted:       %d\n", Adjusted.total[Hs]);
  608.    fprintf(fp,"   D      Depression:                      %d\n", Adjusted.total[D]);
  609.    fprintf(fp,"   Hy     Hysteria:                        %d\n", Adjusted.total[Hy]);
  610.    fprintf(fp,"   Pd+.4K Psychopathic deviant, adjusted:  %d\n", Adjusted.total[Pd]);
  611.    fprintf(fp,"   Mf     Masculinity-femininity:          %d\n", Adjusted.total[(gender == MALE)?Mf_M:Mf_F]);
  612.    fprintf(fp,"   Pa     Paranoia:                        %d\n", Adjusted.total[Pa]);
  613.    fprintf(fp,"   Pt+K   Psychasthenia, adjusted:         %d\n", Adjusted.total[Pt]);
  614.    fprintf(fp,"   Sc+K   Schizophrenia, adjusted:         %d\n", Adjusted.total[Sc]);
  615.    fprintf(fp,"   Ma+.2K Hypomania, adjusted:             %d\n", Adjusted.total[Ma]); 
  616.    fprintf(fp,"   Si     Social introversion:             %d\n", Adjusted.total[Si]);
  617.    fprintf(fp,"   L      Lie scale:                       %d\n", Adjusted.total[L]);
  618.     fprintf(fp,"   Hs     Hypochondriasis, base:           %d\n", results.total[Hs]);
  619.    fprintf(fp,"   Pd     Psychopathic deviant, base:      %d\n", results.total[Pd]);
  620.    fprintf(fp,"   Pt     Psychasthenia, base:             %d\n", results.total[Pt]);
  621.    fprintf(fp,"   Sc     Schizophrenia, base:             %d\n", results.total[Sc]);
  622.    fprintf(fp,"   Ma     Hypomania, base:                 %d\n", results.total[Ma]);
  623.    fprintf(fp,"------------------------------------------------\n\n");
  624.     fprintf(fp,"     L   F   K   1   2   3   4   5   6   7   8   9   0\n");
  625.    
  626.     int line;
  627.     char lineBuf[64];
  628.     int ref;
  629.    // Cross reference MMPITotal array to build chart
  630.    static int chartXRef[]={
  631.       L,
  632.       F,
  633.       K,
  634.       Hs,
  635.       D,
  636.       Hy,
  637.         Pd,
  638.         Mf_M,
  639.         Pa,
  640.         Pt,
  641.         Sc,
  642.         Ma,
  643.         Si
  644.         };
  645.    chartXRef[7] = (gender == MALE)?Mf_M:Mf_F;
  646.    int val;
  647.  
  648.     for(line=24 ;line >= 4;line--) {
  649.         
  650.         for(ref=0;ref<50;++ref)
  651.             lineBuf[ref]=' ';
  652.  
  653.         for(ref=0;ref<13;++ref) {
  654.             val = Scale.total[chartXRef[ref]]/5; 
  655.          if(val == line) {
  656.             lineBuf[ref*4]='x';
  657.             }
  658.          else
  659.             if( (line == 24) && (val > line) )
  660.                lineBuf[ref*4]='^';
  661.             else
  662.                if( (line == 4) && (val < line) )
  663.                   lineBuf[ref*4]='v';
  664.                   else
  665.                       lineBuf[ref*4]='.';
  666.             }
  667.  
  668.         if((line==6)||(line==14))
  669.             for(ref=0;ref<50;++ref)
  670.                 switch(lineBuf[ref])
  671.                     {
  672.                     case '.':
  673.                         lineBuf[ref]='-';
  674.                         break;
  675.                    case ' ':
  676.                         lineBuf[ref]='-';
  677.                         break;
  678.                     }
  679.       lineBuf[50]='\0';
  680.         fprintf( fp, "%4d %.49s %-4d\n", line*5, lineBuf, line*5 );
  681.         }
  682.    fprintf(fp,"     L   F   K   Hs  D   Hy  Pd  Mf  Pa  Pt  Sc  Ma  Si\n");
  683.    fclose(fp);
  684.    return(1);
  685. }
  686.